home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 8: LINUX Games / Linux Cubed Series 8 - LINUX Games.iso / games / x11 / rpg / crossfir.000 / crossfir / crossfire-0.92.4.client / xutil.c < prev   
C/C++ Source or Header  |  1996-04-21  |  24KB  |  881 lines

  1. #include <X11/keysym.h>
  2. #include "def-keys.h"
  3. /* This contains varous 'support' functions.  These functions will probably
  4.  * go mostly unaltered between different toolkits, as long as X11 is still
  5.  * used.  This file is not compiled seperately, rather it is included by
  6.  * x11.c, so all statics will still work fine.
  7.  */
  8.  
  9. static char *colorname[] = {
  10. "Black",                /* 0  */
  11. "White",                /* 1  */
  12. "Navy",                 /* 2  */
  13. "Red",                  /* 3  */
  14. "Orange",               /* 4  */
  15. "DodgerBlue",           /* 5  */
  16. "DarkOrange2",          /* 6  */
  17. "SeaGreen",             /* 7  */
  18. "DarkSeaGreen",         /* 8  */        /* Used for window background color */
  19. "Grey50",               /* 9  */
  20. "Sienna",               /* 10 */
  21. "Gold",                 /* 11 */
  22. "Khaki"                 /* 12 */
  23. };
  24.  
  25.  
  26. static int allocate_colors(Display *disp, Window w, long screen_num,
  27.         Colormap *colormap, XColor discolor[16])
  28. {
  29.   int i, tried = 0, depth=0, iscolor;
  30.   Status status;
  31.   Visual *vis;
  32.   XColor exactcolor;
  33.  
  34.   iscolor = 1;
  35.   vis = DefaultVisual(disp,screen_num);
  36.   if (vis->class >= StaticColor) {
  37.     *colormap = DefaultColormap(disp,screen_num);
  38.     depth = DefaultDepth(disp,screen_num);
  39.   }
  40.   else {
  41.     printf("Switching to black and white.\n");
  42.     printf("You have a black and white terminal.\n");
  43.     return 0;
  44.   }
  45. try_private:
  46.   if (depth > 3 && iscolor) {
  47.     unsigned long pixels[13];
  48.     for (i=0; i<13; i++){
  49.       status = XLookupColor(disp,*colormap, colorname[i],&exactcolor,
  50.                             &discolor[i]);
  51.       if (!status){
  52.         printf("Can't find colour %s.\n", colorname[i]);
  53.         printf("Switching to black and white.\n");
  54.         iscolor = 0;
  55.         break;
  56.       }
  57.       status = XAllocColor(disp,*colormap,&discolor[i]);
  58.       if (!status) {
  59.         if (!tried) {
  60.           printf( "Not enough colours. Trying a private colourmap.\n");
  61.           XFreeColors(disp, *colormap, pixels, i-1, 0);
  62.           *colormap = XCreateColormap(disp, w, vis, AllocNone);
  63.           XSetWindowColormap(disp, w, *colormap);
  64.           tried = 1;
  65.           goto try_private;
  66.         } else {
  67.           printf( "Failed. Switching to black and white.\n");
  68.           iscolor = 0;
  69.           break;
  70.         }
  71.       }
  72.       pixels[i] = discolor[i].pixel;
  73.     }
  74.   }
  75.   return iscolor;
  76. }
  77.  
  78. /*
  79.  * This function adds the path to the fontpath of the given display.
  80.  * It's mostly copied from the X11R5 distribution.
  81.  */
  82.  
  83. static void set_font_path(Display *dpy, char *path) {
  84.   char **currentList = NULL; int ncurrent = 0;
  85.   char **directoryList = NULL; int ndirs = 0;
  86.  
  87.   currentList = XGetFontPath (dpy, &ncurrent);
  88.   if(currentList==NULL) {
  89.     fprintf(stderr,"Unable to get old font path.\n");
  90.     return;
  91.   }
  92.   {
  93.     register char *cp = path;
  94.     ndirs=1;
  95.     while((cp=strchr(cp, ','))!=NULL)
  96.       ndirs++,cp++;
  97.     directoryList=(char **) malloc(ndirs*sizeof(char *));
  98.     if(!directoryList) {
  99.       fprintf(stderr,"Unable to allocate memory for font path directory.\n");
  100.       return;
  101.     }
  102.   }
  103.   {
  104.     int i=0;
  105.     char *cp = path;
  106.     directoryList[i++]=cp;
  107.     while((cp=strchr(cp, ','))!=NULL)
  108.       directoryList[i++]=cp+1,
  109.       *cp++='\0';
  110.     if(i!=ndirs) {
  111.       fprintf(stderr,"Internal error, only parsed %d of %d dirs.\n",i,ndirs);
  112.       return;
  113.     }
  114.   }
  115.   {
  116.     int nnew=ndirs+ncurrent;
  117.     char **newList = (char **) malloc (nnew * sizeof(char *));
  118.  
  119.     if(!newList) {
  120.       fprintf(stderr,"Couldn't get memory for new fontpath.\n");
  121.       return;
  122.     }
  123. /* #if defined(SYSV) || defined(SVR4) */
  124.     memcpy((void *)newList,(void *)directoryList,
  125.            (unsigned) (ndirs*sizeof (char *)));
  126.     memcpy((void *) (newList + ndirs), (void *) currentList,
  127.            (unsigned) (ncurrent*sizeof (char *)));
  128.     XSetFontPath(dpy,newList, nnew);
  129.     free((char *)newList);
  130.   }
  131.   if (directoryList)
  132.     free((char *) directoryList);
  133.   if (currentList)
  134.     XFreeFontPath (currentList);
  135. }
  136.  
  137. /*
  138.  * Checks if "crossfire" is present somewhere in the fontpath of
  139.  * the given display.
  140.  */
  141.  
  142. static int check_font_path(Display *dpy) {
  143.   int count;
  144.   char **list;
  145.  
  146.   list = XListFonts(dpy, font_graphic, 1, &count);
  147.   fprintf(stderr, "Matching fonts to %s: %d (%s)\n",
  148.       font_graphic,count,count?*list:"");
  149.   XFreeFontNames(list);
  150.   return count;
  151. }
  152.  
  153. /*
  154.  * Uses check_font_path() and set_font_path() to check and, if needed
  155.  * fix the fontpath for the player.
  156.  * Function changed around to make it useful for the client.
  157.  * Passing the player struct to this is not required - xio.c
  158.  * can use the return value to set the use_pixmaps value in the
  159.  * player struct.  name is only passed to give better error
  160.  * messages.
  161.  */
  162.  
  163. static int fixfontpath(Display *disp, char *name) {
  164.  
  165.   if (check_font_path(disp))
  166.     return 0;
  167.  
  168.   fprintf(stderr,"Trying to fix fontpath for display %s.\n",name);
  169.   set_font_path(disp,FONTDIR);
  170.   if(check_font_path(disp))
  171.      return 0;
  172.   fprintf(stderr,"Failed, switching to pixmaps (this might take a while).\n");
  173.   return 1;
  174. }
  175.  
  176.  
  177.  
  178. /***********************************************************************
  179.  *
  180.  * Key board input translations are handled here.  We don't deal with
  181.  * the events, but rather KeyCodes and KeySyms.
  182.  *
  183.  * It would be nice to deal with only KeySyms, but many keyboards
  184.  * have keys that do not correspond to a KeySym, so we do need to
  185.  * support KeyCodes.
  186.  *
  187.  ***********************************************************************/
  188.  
  189.  
  190. static KeyCode firekey[2], runkey[2], commandkey, *bind_keycode;
  191. static KeySym firekeysym[2], runkeysym[2], commandkeysym,*bind_keysym;
  192. static int bind_flags=0;
  193. static char bind_buf[MAX_BUF];
  194.  
  195. #define KEYF_NORMAL    0x01    /* Used in normal mode */
  196. #define KEYF_FIRE    0x02    /* Used in fire mode */
  197. #define KEYF_RUN    0x04    /* Used in run mode */
  198. #define KEYF_MODIFIERS    0x07    /* Mask for actual keyboard modifiers, */
  199.                 /* not action modifiers */
  200. #define KEYF_EDIT    0x08    /* Line editor */
  201.  
  202. extern char *directions[9];
  203.  
  204.  
  205. typedef struct Keys {
  206.     uint8    flags;
  207.     sint8    direction;
  208.     KeySym    keysym;
  209.     char    *command;
  210.     struct Keys    *next;
  211. } Key_Entry;
  212.  
  213. #if 0
  214. static void clear_key_entry(Key_Entry *key)
  215. {
  216.     if (key==NULL) return;
  217.     if (key->command) {
  218.     free(key->command);
  219.     key->command=NULL;
  220.     }
  221.     key->flags=0;
  222.     key->direction=-1;
  223.     key->next=NULL;
  224.     key->keysym=NoSymbol;
  225. }
  226. #endif
  227. /* Key codes can only be from 8-255 (at least according to
  228.  * the X11 manual.  This is easier than using a hash
  229.  * table, quicker, and doesn't use much more space.
  230.  */
  231.  
  232. #define MAX_KEYCODE 255
  233. static Key_Entry *keys[256];
  234.  
  235.  
  236.  
  237.  
  238. /* Updates the keys array with the keybinding that is passed.  All the
  239.  * arguments are pretty self explanatory.  flags is the various state
  240.  * that the keyboard is in.
  241.  */
  242. static void insert_key(KeySym keysym, KeyCode keycode, int flags, char *command)
  243. {
  244.  
  245.     Key_Entry *newkey;
  246.     int i, direction=-1;
  247.  
  248.     if (keycode>MAX_KEYCODE) {
  249.     fprintf(stderr,"Warning insert_key:keycode that is passed is greater than 255.\n");
  250.     keycode=0;    /* hopefully the rest of the data is OK */
  251.     }
  252.     if (keys[keycode]==NULL) {
  253.     keys[keycode]=malloc(sizeof(Key_Entry));
  254.     keys[keycode]->command=NULL;
  255.     keys[keycode]->next=NULL;
  256.     }
  257.     newkey=keys[keycode];
  258.  
  259.     /* Try to find out if the command is a direction command.  If so, we
  260.      * then want to keep track of this fact, so in fire or run mode,
  261.      * things work correctly.
  262.      */
  263.     for (i=0; i<9; i++)
  264.     if (!strcmp(command, directions[i])) {
  265.         direction=i;
  266.         break;
  267.     }
  268.  
  269.     if (keys[keycode]->command!=NULL) {
  270.     /* if keys[keycode]->command is not null, then newkey is
  271.      * the same as keys[keycode]->command.
  272.      */
  273.     while (newkey->next!=NULL)
  274.         newkey = newkey->next;
  275.     newkey->next = malloc(sizeof(Key_Entry));
  276.     newkey = newkey->next;
  277.     /* This is the only initializing we need to do - the other fields
  278.      * will get filled in by the passed parameters
  279.      */
  280.     newkey->next = NULL;
  281.     }
  282.     newkey->keysym = keysym;
  283.     newkey->flags = flags;
  284.     newkey->command = strdup_local(command);
  285.     newkey->direction = direction;
  286. }
  287.  
  288.  
  289. static void parse_keybind_line(char *buf, int line)
  290. {
  291.     char *cp, *cpnext;
  292.     KeySym keysym;
  293.     KeyCode keycode;
  294.     int flags;
  295.  
  296.     if (buf[0]=='#' || buf[0]=='\n') return;
  297.     if ((cpnext = strchr(buf,' '))==NULL) {
  298.     fprintf(stderr,"Line %d (%s) corrupted in keybinding file.\n", line,buf);
  299.     return;
  300.     }
  301.     *cpnext++ = '\0';
  302.     keysym = XStringToKeysym(buf);
  303.     cp = cpnext;
  304.     if ((cpnext = strchr(cp,' '))==NULL) {
  305.     fprintf(stderr,"Line %d (%s) corrupted in keybinding file.\n", line, cp);
  306.     return;
  307.     }
  308.     *cpnext++ = '\0';
  309.  
  310.     /* If we can, convert the keysym into a keycode.  */
  311.     keycode = atoi(cp);
  312.     if (keysym!=NoSymbol) {
  313.         keycode = XKeysymToKeycode(display, keysym);
  314.  
  315.         /* It is possible that we get a keysym that we can not convert
  316.          * into a keycode (such a case might be binding the key on
  317.          * one system, and later trying to run on another system that
  318.          * doesn't have that key.
  319.          * While the client will not be able to use it this invocation,
  320.          * it may be able to use it in the future.  As such, don't throw
  321.          * it away, but at least print a warning message.
  322.          */
  323.         if (keycode==0) {
  324.     fprintf(stderr,"Warning: could not convert keysym %s into keycode, ignoring\n",
  325.         buf);
  326.         }
  327.     }
  328.     cp = cpnext;
  329.     if ((cpnext = strchr(cp,' '))==NULL) {
  330.     fprintf(stderr,"Line %d (%s) corrupted in keybinding file.\n", line, cp);
  331.     return;
  332.     }
  333.     *cpnext++ = '\0';
  334.     flags = 0;
  335.     while (*cp!='\0') {
  336.         switch (*cp) {
  337.     case 'A':
  338.         flags |= KEYF_NORMAL | KEYF_FIRE | KEYF_RUN;
  339.         break;
  340.     case 'N':
  341.         flags |= KEYF_NORMAL;
  342.         break;
  343.     case 'F':
  344.         flags |= KEYF_FIRE;
  345.         break;
  346.     case 'R':
  347.         flags |= KEYF_RUN;
  348.         break;
  349.     case 'E':
  350.         flags |= KEYF_EDIT;
  351.         break;
  352.     default:
  353.         fprintf(stderr,"Warning:  Unknown flag (%c) line %d in key binding file\n",
  354.         *cp, line);
  355.         }
  356.         cp++;
  357.     }
  358.     /* Rest of the line is the actual command.  Lets kill the newline */
  359.     cpnext[strlen(cpnext)-1]='\0';
  360.     insert_key(keysym, keycode, flags, cpnext);
  361. }
  362.  
  363. static void init_default_keybindings()
  364. {
  365.   char buf[MAX_BUF];
  366.   int i;
  367.  
  368.   for(i=0;i< sizeof(def_keys)/sizeof(char *);i++) {
  369.     strcpy(buf,def_keys[i]);
  370.     parse_keybind_line(buf,i);
  371.   }
  372. }
  373.  
  374.  
  375. /* This reads in the keybindings, and initializes any special values.
  376.  * called by init_windows.
  377.  */
  378.  
  379. static void init_keys()
  380. {
  381.     int i, line=0;
  382.     FILE *fp;
  383.     char buf[MAX_BUF];
  384.  
  385.     commandkeysym = XK_apostrophe;
  386.     commandkey =XKeysymToKeycode(display,XK_apostrophe);
  387.     if (!commandkey) {
  388.       commandkeysym =XK_acute;
  389.       commandkey =XKeysymToKeycode(display, XK_acute);
  390.     }
  391.     firekeysym[0] =XK_Shift_L;
  392.     firekey[0] =XKeysymToKeycode(display, XK_Shift_L);
  393.     firekeysym[1] =XK_Shift_R;
  394.     firekey[1] =XKeysymToKeycode(display, XK_Shift_R);
  395.     runkeysym[0]  =XK_Control_L;
  396.     runkey[0]  =XKeysymToKeycode(display, XK_Control_L);
  397.     runkeysym[1]  =XK_Control_R;
  398.     runkey[1]  =XKeysymToKeycode(display, XK_Control_R);
  399.  
  400.     for (i=0; i<=MAX_KEYCODE; i++) {
  401.     keys[i] = NULL;
  402.     }
  403.  
  404.     /* We now try to load the keybindings.  First place to look is the
  405.      * users home directory, "~/.crossfire/keys".  Using a directory
  406.      * seems like a good idea, in the future, additional stuff may be
  407.      * stored.
  408.      *
  409.      * The format is described in the def_keys file.  Note that this file
  410.      * is the same as what it was in the server distribution.  To convert
  411.      * bindings in character files to this format, all that needs to be done
  412.      * is remove the 'key ' at the start of each line.
  413.      *
  414.      * We need at least one of these keybinding files to exist - this is
  415.      * where the various commands are defined.  In theory, we actually
  416.      * don't need to have any of these defined -- the player could just
  417.      * bind everything.  Probably not a good idea, however.
  418.      */
  419.  
  420.     sprintf(buf,"%s/.crossfire/keys", getenv("HOME"));
  421.     if ((fp=fopen(buf,"r"))==NULL) {
  422.     fprintf(stderr,"Could not open ~/.crossfire/keys, trying to load global bindings\n");
  423.     if (client_libdir==NULL) {
  424.         init_default_keybindings();
  425.         return;
  426.     }
  427.     sprintf(buf,"%s/def_keys", client_libdir);
  428.     if ((fp=fopen(buf,"r"))==NULL) {
  429.         init_default_keybindings();
  430.         return;
  431.     }
  432.     }
  433.     while (fgets(buf, MAX_BUF, fp)) {
  434.     line++;
  435.     parse_keybind_line(buf,line);
  436.     }
  437.     fclose(fp);
  438. }
  439.  
  440. /* The only things we actually care about is the run and fire keys.
  441.  * Other key releases are not important.
  442.  * If it is the release of a run or fire key, we tell the client
  443.  * to stop firing or running.  In some cases, it is possible that we
  444.  * actually are not running or firing, and in such cases, the server
  445.  * will just ignore the command.
  446.  */
  447.  
  448.  
  449. static void parse_key_release(KeyCode kc, KeySym ks) {
  450.  
  451.     /* Only send stop firing/running commands if we are in actual
  452.      * play mode.  Something smart does need to be done when the character
  453.      * enters a non play mode with fire or run mode already set, however.
  454.      */
  455.     if (cpl.input_state != Playing) return;
  456.  
  457.     if (kc==firekey[0] || ks==firekeysym[0] || 
  458.     kc==firekey[1] || ks==firekeysym[1]) {
  459. #if 0    /* Nice idea, but unfortunately prints too many false results */
  460.         if (cpl.echo_bindings) draw_info("stop fire",NDI_BLACK);
  461. #endif
  462.         cpl.fire_on=0;
  463.         stop_fire();
  464.     }
  465.     else if (kc==runkey[0] || ks==runkeysym[0] ||
  466.     kc==runkey[1] || ks==runkeysym[1]) {
  467.         cpl.run_on=0;
  468.         if (cpl.echo_bindings) draw_info("stop run",NDI_BLACK);
  469.         stop_run();
  470.     }
  471. }
  472.  
  473. /* This parses a keypress.  It should only be called win in Playing
  474.  * mode.
  475.  */
  476. static void parse_key(char key, KeyCode keycode, KeySym keysym)
  477. {
  478.     Key_Entry *keyentry, *first_match=NULL;
  479.     int present_flags=0;
  480.     char buf[MAX_BUF];
  481.  
  482.     if (keycode == commandkey && keysym==commandkeysym) {
  483.     draw_prompt(">");
  484.     cpl.input_state = Command_Mode;
  485.     cpl.no_echo=FALSE;
  486.     return;
  487.     }
  488.     if (keycode == firekey[0] || keysym==firekeysym[0] ||
  489.     keycode == firekey[1] || keysym==firekeysym[1]) {
  490.         cpl.fire_on=1;
  491.         return;
  492.     }
  493.     if (keycode == runkey[0] || keysym==runkeysym[0] ||
  494.     keycode==runkey[1] || keysym==runkeysym[1]) {
  495.         cpl.run_on=1;
  496.         return;
  497.     }
  498.  
  499.     if (cpl.run_on) present_flags |= KEYF_RUN;
  500.     if (cpl.fire_on) present_flags |= KEYF_FIRE;
  501.     if (present_flags ==0) present_flags = KEYF_NORMAL;
  502.  
  503.     keyentry = keys[keycode];
  504.     while (keyentry!=NULL) {
  505.     if ((keyentry->keysym!=NoSymbol && keyentry->keysym!=keysym) ||
  506.         (!(keyentry->flags & present_flags))) {
  507.         keyentry=keyentry->next;
  508.         continue;
  509.         }
  510.     first_match = keyentry;
  511.     /* Try to find a prefect match */
  512.     if ((keyentry->flags & KEYF_MODIFIERS)!= present_flags) {
  513.         keyentry=keyentry->next;
  514.         continue;
  515.     }
  516.     else break;
  517.     }
  518.     if (first_match!=NULL) {
  519.     char buf[MAX_BUF];
  520.  
  521.     if (first_match->flags & KEYF_EDIT) {
  522.         strcpy(cpl.input_text, first_match->command);
  523.         cpl.input_state = Command_Mode;
  524.         draw_prompt(">");
  525.         draw_prompt(cpl.input_text);
  526.         return;
  527.     }
  528.  
  529.     if (first_match->direction>=0) {
  530.         if (cpl.fire_on) {
  531.         sprintf(buf,"fire %s", first_match->command);
  532.         fire_dir(first_match->direction);
  533.         }
  534.         else if (cpl.run_on) {
  535.         run_dir(first_match->direction);
  536.         sprintf(buf,"run %s", first_match->command);
  537.         }
  538.         else {
  539.         strcpy(buf,first_match->command);
  540.         send_command(first_match->command);
  541.         }
  542.         if (cpl.echo_bindings) draw_info(buf,NDI_BLACK);
  543.     }
  544.         else {
  545.         if (cpl.echo_bindings) draw_info(first_match->command,NDI_BLACK);
  546.         send_command(first_match->command);
  547.     }
  548.     return;
  549.     }
  550.     if (key>='0' && key<='9') {
  551.     cpl.count = cpl.count*10 + (key-'0');
  552.     if (cpl.count>100000) cpl.count%=100000;
  553.     return;
  554.     }
  555.     sprintf(buf, "Key unused (%s%s%s)",
  556.           (cpl.fire_on? "Fire&": ""),
  557.           (cpl.run_on ? "Run&" : ""),
  558.           XKeysymToString(keysym));
  559.     draw_info(buf,NDI_BLACK);
  560.     cpl.count=0;
  561. }
  562.  
  563.  
  564. /* This returns a character string desribing the key. */
  565. /* If save_mode is true, it means that the format used for saving
  566.  * the information is used, instead of the usual format for displaying
  567.  * the information in a friendly manner.
  568.  */
  569. static char * get_key_info(Key_Entry *key, KeyCode kc, int save_mode)
  570. {
  571.     static char buf[MAX_BUF];
  572.     char buff[MAX_BUF];
  573.     int bi=0;
  574.  
  575.     if ((key->flags & KEYF_MODIFIERS) == KEYF_MODIFIERS)
  576.     buff[bi++] ='A';
  577.     else {
  578.     if (key->flags & KEYF_NORMAL)
  579.       buff[bi++] ='N';
  580.     if (key->flags & KEYF_FIRE)
  581.       buff[bi++] ='F';
  582.     if (key->flags & KEYF_RUN)
  583.       buff[bi++] ='R';
  584.     }
  585.     if (key->flags & KEYF_EDIT)
  586.     buff[bi++] ='E';
  587.  
  588.     buff[bi]='\0';
  589.     if (save_mode) {
  590.     if(key->keysym == NoSymbol) {
  591.       sprintf(buf, "(null) %i %s %s",
  592.         kc,buff, key->command);
  593.     }
  594.     else {
  595.       sprintf(buf, "%s %i %s %s",
  596.             XKeysymToString(key->keysym), kc,
  597.             buff, key->command);
  598.     }
  599.     }
  600.     else {
  601.     if(key->keysym == NoSymbol) {
  602.       sprintf(buf, "key (null) (%i) %s %s",
  603.         kc,buff, key->command);
  604.     }
  605.     else {
  606.       sprintf(buf, "key %s (%i) %s %s",
  607.             XKeysymToString(key->keysym), kc,
  608.             buff, key->command);
  609.     }
  610.     }
  611.     return buf;
  612. }
  613.  
  614. /* Shows all the keybindings.  The original function from c_bind.c had
  615.  * two modes - one mode which would only show non default bindings, the
  616.  * other where it should show all bindings.  No supported - there is
  617.  * no 'default' bindings - all bindings exist.
  618.  */
  619.  
  620. static void show_keys()
  621. {
  622.   int i, count=1;
  623.   Key_Entry *key;
  624.   char buf[MAX_BUF];
  625.  
  626.   sprintf(buf, "Commandkey %s (%d)", XKeysymToString(commandkeysym),
  627.     commandkey);
  628.   draw_info(buf,NDI_BLACK);
  629.   sprintf(buf, "Firekeys 1: %s (%d), 2: %s (%d)",
  630.       XKeysymToString(firekeysym[0]), firekey[0],
  631.       XKeysymToString(firekeysym[1]), firekey[1]);
  632.   draw_info(buf,NDI_BLACK);
  633.   sprintf(buf, "Runkeys 1: %s (%d), 2: %s (%d)",
  634.       XKeysymToString(runkeysym[0]), runkey[0],
  635.       XKeysymToString(runkeysym[1]), runkey[1]);
  636.   draw_info(buf,NDI_BLACK);
  637.  
  638.  
  639.   /* Perhaps we should start at 8, so0 that we only show 'active'
  640.    * keybindings?
  641.    */
  642.   for (i=0; i<=MAX_KEYCODE; i++) {
  643.     for (key=keys[i]; key!=NULL; key =key->next) {
  644.     sprintf(buf,"%3d %s",count,  get_key_info(key,i,0));
  645.     draw_info(buf,NDI_BLACK);
  646.     count++;
  647.     }
  648.   }
  649. }
  650.  
  651.  
  652.  
  653.  
  654. void bind_key(char *params)
  655. {
  656.   char buf[MAX_BUF];
  657.  
  658.   if (!params) {
  659.     draw_info("Usage: bind [-nfre] {<commandline>/commandkey/firekey{1/2}/runkey{1/2}}",NDI_BLACK);
  660.     return;
  661.   }
  662.  
  663.   if (!strcmp(params, "commandkey")) {
  664.     bind_keycode = &commandkey;
  665.     bind_keysym = &commandkeysym;
  666.     draw_info("Push key to bind new commandkey.",NDI_BLACK);
  667.     cpl.input_state = Configure_Keys;
  668.     return;
  669.   }
  670.   if (!strcmp(params, "firekey1")) {
  671.     bind_keycode = &firekey[0];
  672.     bind_keysym = & firekeysym[0];
  673.     draw_info("Push key to bind new firekey 1.",NDI_BLACK);
  674.     cpl.input_state = Configure_Keys;
  675.     return;
  676.   }
  677.   if (!strcmp(params, "firekey2")) {
  678.     bind_keycode = &firekey[1];
  679.     bind_keysym = & firekeysym[1];
  680.     draw_info("Push key to bind new firekey 2.",NDI_BLACK);
  681.     cpl.input_state = Configure_Keys;
  682.     return;
  683.   }
  684.   if (!strcmp(params, "runkey1")) {
  685.     bind_keycode = &runkey[0];
  686.     bind_keysym = &runkeysym[0];
  687.     draw_info("Push key to bind new runkey 1.",NDI_BLACK);
  688.     cpl.input_state = Configure_Keys;
  689.     return;
  690.   }
  691.   if (!strcmp(params, "runkey2")) {
  692.     bind_keycode = &runkey[1];
  693.     bind_keysym = &runkeysym[1];
  694.     draw_info("Push key to bind new runkey 2.",NDI_BLACK);
  695.     cpl.input_state = Configure_Keys;
  696.     return;
  697.   }
  698.  
  699.   if (params[0] != '-')
  700.     bind_flags =KEYF_MODIFIERS;
  701.   else {
  702.     bind_flags =0;
  703.     bind_keysym=NULL;
  704.     bind_keycode=NULL;
  705.     for (params++; *params != ' '; params++)
  706.       switch (*params) {
  707.       case 'n':
  708.     bind_flags |= KEYF_NORMAL;
  709.     break;
  710.       case 'f':
  711.     bind_flags |= KEYF_FIRE;
  712.     break;
  713.       case 'r':
  714.     bind_flags |= KEYF_RUN;
  715.     break;
  716.       case 'e':
  717.     bind_flags |= KEYF_EDIT;
  718.     break;
  719.       case '\0':
  720.     draw_info("Try unbind to remove bindings..",NDI_BLACK);
  721.     return;
  722.       default:
  723.     sprintf(buf, "Unknown flag to bind: '%c'", *params);
  724.     draw_info(buf,NDI_BLACK);
  725.     return;
  726.       }
  727.     params++;
  728.   }
  729.  
  730.   if (!(bind_flags & KEYF_MODIFIERS))
  731.     bind_flags |= KEYF_MODIFIERS;
  732.  
  733.   if (!params[0]) {
  734.     draw_info("Try unbind to remove bindings..",NDI_BLACK);
  735.     return;
  736.   }
  737.  
  738.   sprintf(buf, "Push key to bind '%s'.", params);
  739.   draw_info(buf,NDI_BLACK);
  740.   strcpy(bind_buf, params);
  741.   bind_keycode=NULL;
  742.   cpl.input_state = Configure_Keys;
  743.   return;
  744. }
  745.  
  746.  
  747. /* This is a recursive function that saves all the entries for a particular
  748.  * entry.  We save the first element first, and then go through
  749.  * and save the rest of the elements.  In this way, the ordering of the key
  750.  * entries in the
  751.  * file remains the same.
  752.  */
  753.  
  754. static void save_individual_key(FILE *fp, Key_Entry *key, KeyCode kc)
  755. {
  756.     if (key==NULL) return;
  757.     fprintf(fp, "%s\n", get_key_info(key, kc, 1));
  758.     save_individual_key(fp, key->next, kc);
  759. }
  760.  
  761. static void save_keys()
  762. {
  763.     char buf[MAX_BUF], buf2[MAX_BUF];
  764.     int i;
  765.     FILE *fp;
  766.  
  767.     sprintf(buf,"%s/.crossfire/keys", getenv("HOME"));
  768.     if ((fp=fopen(buf,"w"))==NULL) {
  769.     sprintf(buf2,"Could not open %s, key bindings not saved\n", buf);
  770.     draw_info(buf2,NDI_BLACK);
  771.     return;
  772.     }
  773.  
  774.     for (i=0; i<=MAX_KEYCODE; i++) {
  775.     save_individual_key(fp, keys[i], i);
  776.     }
  777.     fclose(fp);
  778.     /* Should probably check return value on all writes to be sure, but... */
  779.     draw_info("key bindings successfully saved.",NDI_BLACK);
  780. }
  781.  
  782. static void configure_keys(KeyCode k, KeySym keysym)
  783. {
  784.   char buf[MAX_BUF];
  785.  
  786.   if (bind_keycode==NULL) {
  787.     if(k == firekey[0] || k == firekey[1]) {
  788.       cpl.fire_on =1;
  789.       return;
  790.     }
  791.     if(k == runkey[0] || k == runkey[1]) {
  792.       cpl.run_on =1;
  793.       return;
  794.     }
  795.   }
  796.   cpl.input_state = Playing;
  797.   /* Try to be clever - take into account shift/control keys being
  798.    * held down when binding keys - in this way, player does not have to use
  799.    * -f and -r flags to bind for many simple binds.
  800.    */
  801.     
  802.   if ((cpl.fire_on || cpl.run_on) && (bind_flags & KEYF_MODIFIERS)==KEYF_MODIFIERS) {
  803.     bind_flags &= ~KEYF_MODIFIERS;
  804.     if (cpl.fire_on) bind_flags |= KEYF_FIRE;
  805.     if (cpl.run_on) bind_flags |= KEYF_RUN;
  806.   }
  807.  
  808.   if (bind_keycode!=NULL) {
  809.     *bind_keycode = k;
  810.     *bind_keysym=keysym;
  811.   }
  812.   else {
  813.     insert_key(keysym, k, bind_flags, bind_buf);
  814.   }
  815.  
  816.   sprintf(buf, "Binded to key '%s' (%i)", XKeysymToString(keysym), (int)k);
  817.   draw_info(buf,NDI_BLACK);
  818.   cpl.fire_on=0;
  819.   cpl.run_on=0;
  820.  
  821.   /* Do this each time a new key is bound.  This way, we are never actually
  822.    * storing any information that needs to be saved when the connection
  823.    * dies or the player quits.
  824.    */
  825.   save_keys();
  826.   return;
  827. }
  828.  
  829.  
  830. void unbind_key(char *params)
  831. {
  832.   int count=0, keyentry, onkey;
  833.   Key_Entry *key, *tmp;
  834.   char buf[MAX_BUF];
  835.  
  836.   if (params==NULL || params[0]=='\0') {
  837.     show_keys();
  838.     return;
  839.   }
  840.   if ((keyentry=atoi(params))==0) {
  841.     draw_info("Usage: unbind <entry_number>",NDI_BLACK);
  842.     return;
  843.   }
  844.  
  845.   for (onkey=0; onkey<=MAX_KEYCODE; onkey++) {
  846.     for (key=keys[onkey]; key; key =key->next) {
  847.       count++;
  848.       if (keyentry==count) {
  849.     if (key == keys[onkey]) {
  850.         keys[onkey] = key->next;
  851.         goto unbinded;
  852.     }
  853.         for (tmp=keys[onkey]; tmp->next!=NULL; tmp=tmp->next) {
  854.         if (tmp->next == key) {
  855.         tmp->next =key->next;
  856.         goto unbinded;
  857.         }
  858.     }
  859.         fprintf(stderr,"unbind_key - found number entry, but could not find actual key\n");
  860.       }
  861.     }
  862.   }
  863.   /* Makes things look better to draw the blank line */
  864.   draw_info("",NDI_BLACK);
  865.   draw_info("No such entry. Try 'unbind' with no options to find entry.",NDI_BLACK);
  866.   return;
  867.  
  868.   /*
  869.    * Found. Now remove it.
  870.    */
  871.  unbinded:
  872.  
  873.     sprintf(buf,"Removed binding: %3d %s", count, get_key_info(key,onkey,0));
  874.  
  875.     draw_info(buf,NDI_BLACK);
  876.     if (key->command) free(key->command);
  877.     free(key);
  878.     save_keys();
  879. }
  880.  
  881.